#ifndef BOOST__THREAD__DETAILS__UNILOCK_INTERFACE_IMPL_HPP
#  define BOOST__THREAD__DETAILS__UNILOCK_INTERFACE_IMPL_HPP

// Copyright (C) 2004-2009
// Alex Turbov, Igor Veresov
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#  include <boost/thread/condition.hpp>
#  include <boost/thread/mutex.hpp>

#  include <boost/call_traits.hpp>

namespace boost { namespace details {

template <typename Scheduler>
class unilock_interface_impl
{
public:
    typedef Scheduler scheduler_type;
    typedef BOOST_DEDUCED_TYPENAME scheduler_type::lock_params_type lock_params_type;
    typedef BOOST_DEDUCED_TYPENAME scheduler_type::unlock_params_type unlock_params_type;

private:
    mutex m_mut;
    condition m_cond;
    scheduler_type m_sched;

    typedef bool (unilock_interface_impl::*lock_func_t)(
        BOOST_DEDUCED_TYPENAME call_traits<lock_params_type>::param_type
      , int
      , mutex::scoped_lock&
      );

    bool lock_impl(
        BOOST_DEDUCED_TYPENAME call_traits<lock_params_type>::param_type p
      , int request_id
      , mutex::scoped_lock& l
      )
    {
        while (!m_sched.try_lock(p, request_id))
            m_cond.wait(l);
        return true;
    }
    bool try_lock_impl(
        BOOST_DEDUCED_TYPENAME call_traits<lock_params_type>::param_type p
      , int request_id
      , mutex::scoped_lock&
      )
    {
        return m_sched.try_lock(p, request_id);
    }

    bool lock_decorator(BOOST_DEDUCED_TYPENAME call_traits<lock_params_type>::param_type p, lock_func_t lf)
    {
        bool result = false;
        {
            mutex::scoped_lock l(m_mut);                    // may throw thread_resource_error
            int request_id;
            try
            {
                request_id = m_sched.assign_request_id(p);
            }
            catch (...)
            {
                throw thread_resource_error();
            }
            try
            {
                result = (this->*lf)(p, request_id, l);     // call decorated member function
            }
            catch (const thread_interrupted& e)
            {
                try
                {
                    m_sched.unassign_request_id(p, request_id);
                }
                catch (...)
                {
                    throw e;                                // rethrow thread_interrupted anyway
                }
            }
            try
            {
                m_sched.unassign_request_id(p, request_id);
            }
            catch (...)
            {
                throw thread_resource_error();
            }
        }
        m_cond.notify_all();
        return result;
    }

public:
    void lock(BOOST_DEDUCED_TYPENAME call_traits<lock_params_type>::param_type p)
    {
        lock_decorator(p, &unilock_interface_impl::lock_impl);
    }
    bool try_lock(BOOST_DEDUCED_TYPENAME call_traits<lock_params_type>::param_type p)
    {
        return lock_decorator(p, &unilock_interface_impl::try_lock_impl);
    }
    void unlock(BOOST_DEDUCED_TYPENAME call_traits<unlock_params_type>::param_type p)
    {
        try
        {
            mutex::scoped_lock l(m_mut);                    // may throw thread_resource_error
            m_sched.unlock(p);
        }
        catch (...)
        {
            throw thread_resource_error();
        }
        m_cond.notify_all();                                // NOTE: Or maybe better to notify one?
    }
};

}}                                                          // namespace details, boost
#endif                                                      // #ifndef BOOST__THREAD__DETAILS__UNILOCK_INTERFACE_IMPL_HPP
